﻿using System;
using System.Collections.Generic;
using System.Text;

using NHibernate;
using NHibernate.Criterion;

using Pink.Scheduler.RemoteInterfaces;
using Pink.Scheduler.DataAccess;
using Pink.Scheduler.RemoteInterfaces.Enum;

namespace Pink.Scheduler.RemoteInterfaces
{
	/// <summary>
	/// Расписание выполнения задач
	/// </summary>
	internal class Schedule
	{
		private ISessionFactory sessionFactory;
		/// <summary>
		/// Конструктр
		/// </summary>
		/// <param name="factory"></param>
		public Schedule(ISessionFactory factory)
		{
			sessionFactory = factory;
		}
		/// <summary>
		/// Узнать, когда следующее выполнение задачи по расписанию
		/// </summary>
		/// <param name="task"></param>
		/// <returns></returns>
		public TimeSpan GetTaskDue(TaskData task)
		{
			using (ISession session = sessionFactory.OpenSession())
			{
				TaskData thisSessionTask = session.Get<TaskData>(task.ID);
				// расписания не применяются, запуск только вручную
				if (!thisSessionTask.IsEnabled || thisSessionTask.ScheduleType == ScheduleType.Manual)
					return TimeSpan.FromMilliseconds(-1);
				// время последнего назначения на выполнение
				TaskExecution lastExecution = session.CreateCriteria(typeof(TaskExecution))
					.Add(Expression.Eq(TaskExecution.TaskProp, thisSessionTask))
					.Add(Expression.Le(TaskExecution.StartTimeProp, DateTime.Now))
					.AddOrder(Order.Desc(TaskExecution.StartTimeProp))
					.SetMaxResults(1)
					.UniqueResult<TaskExecution>();
				TimeSpan scheduleTime = TimeSpan.FromSeconds(task.ScheduleTimeSeconds);
				switch (thisSessionTask.ScheduleType)
				{
					case ScheduleType.Periodic:
						// если раньше не запускались, то запуститься сразу
						if (lastExecution == null)
							return TimeSpan.FromSeconds(0);
						else
						{
							// если уже запущена, то запуск не раньше, чем через полный период
							if (lastExecution.State == ExecutionState.Created ||
								lastExecution.State == ExecutionState.Running)
								return scheduleTime;
							TimeSpan passed = DateTime.Now - lastExecution.StartTime;
							// если прошло больше чем надо, то выполнять пора сейчас
							if (passed > scheduleTime)
								return TimeSpan.FromSeconds(0);
							else
								return scheduleTime - passed;
						}
					case ScheduleType.Daily:
						TimeSpan current = DateTime.Now.TimeOfDay;
						// если уже прошло - то ждать до завтра
						return current > scheduleTime ?
							TimeSpan.FromDays(1) - current + scheduleTime :
							scheduleTime - current;
					default:
						return TimeSpan.FromMilliseconds(-1);
				}
			}
		}

		/// <summary>
		/// Отформатировать интервал времени
		/// </summary>
		public static string FormatTimespan(TimeSpan period)
		{
			string result = string.Empty;

			int days = period.Days;
			if (days > 0)
				result += string.Format("{0} {1}", days, "дней");

			int hours = period.Hours;
			if (hours > 0)
				result += string.Format(" {0} {1}", hours, "часов");

			int minutes = period.Minutes;
			if (minutes > 0)
				result += string.Format(" {0} {1}", minutes, "минут");

			int seconds = period.Seconds;
			if (seconds > 0)
				result += string.Format(" {0} {1}", seconds, "секунд");

			return result;
		}
	}
}
